package com.wust.modules.poi.excel.export.base;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;

import com.wust.modules.poi.excel.entity.enmus.ExcelType;
import com.wust.modules.poi.excel.entity.params.ExcelExportEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import com.wust.modules.poi.excel.export.styler.IExcelExportStyler;
import com.wust.modules.poi.util.PoiMergeCellUtil;
import com.wust.modules.poi.util.PoiPublicUtil;

/**
 * @author wanheng
 */
@Slf4j
public abstract class AbstractExcelExportBase extends ExportBase {
    
    private int currentIndex = 0;
    protected ExcelType type;
    private final Map<Integer, Double> statistics;
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
    private IExcelExportStyler excelExportStyler;

    public AbstractExcelExportBase() {
        this.type = ExcelType.HSSF;
        this.statistics = new HashMap();
    }

    public int createCells(Drawing patriarch, int index, Object t, List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook, short rowHeight) throws Exception {
        Row row = sheet.createRow(index);
        row.setHeight(rowHeight);
        int maxHeight = 1;
        int cellNum = 0;
        int indexKey = this.createIndexCell(row, index, excelParams.get(0));
        cellNum = cellNum + indexKey;
        int k = indexKey;

        ExcelExportEntity entity;
        int paramSize;
        for(paramSize = excelParams.size(); k < paramSize; ++k) {
            entity = excelParams.get(k);
            if (!entity.isSubColumn()) {
                Iterator iterator;
                if (entity.isMergeColumn()) {
                    Map<String, Object> subColumnMap = new HashMap(16);
                    List<String> mapKeys = entity.getSubColumnList();

                    Object subKeyValue;
                    String subKey;
                    for(iterator = mapKeys.iterator(); iterator.hasNext(); subColumnMap.put(subKey, subKeyValue)) {
                        subKey = (String) iterator.next();
                        if (t instanceof Map) {
                            subKeyValue = ((Map)t).get(subKey);
                        } else {
                            subKeyValue = PoiPublicUtil.getParamsValue(subKey, t);
                        }
                    }

                    this.createListCells(patriarch, index, cellNum, subColumnMap, entity.getList(), sheet, workbook);
                    cellNum += entity.getSubColumnList().size();
                } else if (entity.getList() == null) {
                    Object value = this.getCellValue(entity, t);
                    if (entity.getType() == 1) {
                        this.createStringCell(row, cellNum++, value == null ? "" : value.toString(), index % 2 == 0 ? this.getStyles(false, entity) : this.getStyles(true, entity), entity);
                    } else if (entity.getType() == 4) {
                        this.createNumericCell(row, cellNum++, value == null ? "" : value.toString(), index % 2 == 0 ? this.getStyles(false, entity) : this.getStyles(true, entity), entity);
                    } else {
                        this.createImageCell(patriarch, entity, row, cellNum++, value == null ? "" : value.toString(), t);
                    }
                } else {
                    Collection<?> list = this.getListCellValue(entity, t);
                    int listC = 0;

                    for(iterator = list.iterator(); iterator.hasNext(); ++listC) {
                        Object obj = iterator.next();
                        this.createListCells(patriarch, index + listC, cellNum, obj, entity.getList(), sheet, workbook);
                    }

                    cellNum += entity.getList().size();
                    if (list.size() > maxHeight) {
                        maxHeight = list.size();
                    }
                }
            }
        }

        cellNum = 0;
        k = indexKey;

        for(paramSize = excelParams.size(); k < paramSize; ++k) {
            entity = excelParams.get(k);
            if (entity.getList() != null) {
                cellNum += entity.getList().size();
            } else if (entity.isNeedMerge()) {
                for(int i = index + 1; i < index + maxHeight; ++i) {
                    sheet.getRow(i).createCell(cellNum);
                    sheet.getRow(i).getCell(cellNum).setCellStyle(this.getStyles(false, entity));
                }

                sheet.addMergedRegion(new CellRangeAddress(index, index + maxHeight - 1, cellNum, cellNum));
                ++cellNum;
            }
        }

        return maxHeight;
    }

    public void createImageCell(Drawing patriarch, ExcelExportEntity entity, Row row, int i, String imagePath, Object obj) throws Exception {
        row.setHeight((short)((int)(50.0D * entity.getHeight())));
        row.createCell(i);
        ClientAnchor anchor;
        if (this.type.equals(ExcelType.HSSF)) {
            anchor = new HSSFClientAnchor(0, 0, 0, 0, (short)i, row.getRowNum(), (short)(i + 1), row.getRowNum() + 1);
        } else {
            anchor = new XSSFClientAnchor(0, 0, 0, 0, (short)i, row.getRowNum(), (short)(i + 1), row.getRowNum() + 1);
        }

        if (!StringUtils.isEmpty(imagePath)) {
            int imageType = entity.getExportImageType();
            byte[] value = null;
            if (imageType == 2) {
                value = (byte[])(entity.getMethods() != null ? this.getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj));
            } else if (imageType != 4 && !imagePath.startsWith("http")) {
                ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
                String path = null;
                if (imageType == 1) {
                    path = PoiPublicUtil.getWebRootPath(imagePath);
                    log.debug("--- createImageCell getWebRootPath ----filePath--- " + path);
                    path = path.replace("WEB-INF/classes/", "");
                    path = path.replace("file:/", "");
                } else if (imageType == 3) {
                    if (!entity.getImageBasePath().endsWith(File.separator) && !imagePath.startsWith(File.separator)) {
                        path = entity.getImageBasePath() + File.separator + imagePath;
                    } else {
                        path = entity.getImageBasePath() + imagePath;
                    }
                }

                try {
                    assert path != null;
                    BufferedImage bufferImg = ImageIO.read(new File(path));
                    ImageIO.write(bufferImg, imagePath.substring(imagePath.indexOf(".") + 1), byteArrayOut);
                    value = byteArrayOut.toByteArray();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            } else {
                try {
                    if (imagePath.contains(",")) {
                        if (imagePath.startsWith(",")) {
                            imagePath = imagePath.substring(1);
                        }

                        String[] images = imagePath.split(",");
                        imagePath = images[0];
                    }

                    URL url = new URL(imagePath);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    InputStream inStream = conn.getInputStream();
                    value = this.readInputStream(inStream);
                } catch (Exception e) {
                    log.warn(e.getMessage());
                }
            }

            if (value != null) {
                patriarch.createPicture(anchor, row.getSheet().getWorkbook().addPicture(value, this.getImageType(value)));
            }

        }
    }

    private byte[] readInputStream(InputStream inStream) throws Exception {
        if (inStream == null) {
            return null;
        } else {
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];

            int len;
            while((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }

            inStream.close();
            return outStream.toByteArray();
        }
    }

    private int createIndexCell(Row row, int index, ExcelExportEntity excelExportEntity) {
        if ("序号".equals(excelExportEntity.getName()) && "isAddIndex".equals(excelExportEntity.getFormat())) {
            this.createStringCell(row, 0, this.currentIndex + "", index % 2 == 0 ? this.getStyles(false, null) : this.getStyles(true, null), null);
            ++this.currentIndex;
            return 1;
        } else {
            return 0;
        }
    }

    public void createListCells(Drawing patriarch, int index, int cellNum, Object obj, List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook) throws Exception {
        Row row;
        if (sheet.getRow(index) == null) {
            row = sheet.createRow(index);
            row.setHeight(this.getRowHeight(excelParams));
        } else {
            row = sheet.getRow(index);
        }

        int k = 0;

        for(int paramSize = excelParams.size(); k < paramSize; ++k) {
            ExcelExportEntity entity = excelParams.get(k);
            Object value = this.getCellValue(entity, obj);
            if (entity.getType() == 1) {
                this.createStringCell(row, cellNum++, value == null ? "" : value.toString(), row.getRowNum() % 2 == 0 ? this.getStyles(false, entity) : this.getStyles(true, entity), entity);
            } else if (entity.getType() == 4) {
                this.createNumericCell(row, cellNum++, value == null ? "" : value.toString(), index % 2 == 0 ? this.getStyles(false, entity) : this.getStyles(true, entity), entity);
            } else {
                this.createImageCell(patriarch, entity, row, cellNum++, value == null ? "" : value.toString(), obj);
            }
        }

    }

    public void createNumericCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity) {
        Cell cell = row.createCell(index);
        if (StringUtils.isEmpty(text)) {
            cell.setCellValue("");
            cell.setCellType(3);
        } else {
            cell.setCellValue(Double.parseDouble(text));
            cell.setCellType(0);
        }

        if (style != null) {
            cell.setCellStyle(style);
        }

        this.addStatisticsData(index, text, entity);
    }

    public void createStringCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity) {
        Cell cell = row.createCell(index);
        if (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) {
            cell.setCellValue(Double.parseDouble(text));
            cell.setCellType(0);
        } else {
            Object rtext;
            if (this.type.equals(ExcelType.HSSF)) {
                rtext = new HSSFRichTextString(text);
            } else {
                rtext = new XSSFRichTextString(text);
            }

            cell.setCellValue((RichTextString)rtext);
        }

        if (style != null) {
            cell.setCellStyle(style);
        }

        this.addStatisticsData(index, text, entity);
    }

    public void addStatisticsRow(CellStyle styles, Sheet sheet) {
        if (this.statistics.size() > 0) {
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            Set<Integer> keys = this.statistics.keySet();
            this.createStringCell(row, 0, "合计", styles, null);
            Iterator iterator = keys.iterator();

            while(iterator.hasNext()) {
                Integer key = (Integer) iterator.next();
                this.createStringCell(row, key, DOUBLE_FORMAT.format(this.statistics.get(key)), styles, null);
            }

            this.statistics.clear();
        }

    }

    private void addStatisticsData(Integer index, String text, ExcelExportEntity entity) {
        if (entity != null && entity.isStatistics()) {
            Double temp = 0.0D;
            if (!this.statistics.containsKey(index)) {
                this.statistics.put(index, temp);
            }

            try {
                temp = Double.valueOf(text);
            } catch (NumberFormatException ignored) {
            }

            this.statistics.put(index, this.statistics.get(index) + temp);
        }

    }

    public int getFieldWidth(List<ExcelExportEntity> excelParams) {
        int length = -1;

        ExcelExportEntity entity;
        for(Iterator iterator = excelParams.iterator(); iterator.hasNext(); length += entity.getList() != null ? entity.getList().size() : 1) {
            entity = (ExcelExportEntity) iterator.next();
        }

        return length;
    }

    public int getImageType(byte[] value) {
        String type = PoiPublicUtil.getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type)) {
            return 5;
        } else {
            return "PNG".equalsIgnoreCase(type) ? 6 : 5;
        }
    }

    private Map<Integer, int[]> getMergeDataMap(List<ExcelExportEntity> excelParams) {
        Map<Integer, int[]> mergeMap = new HashMap(16);
        int i = 0;
        Iterator iterator = excelParams.iterator();

        while(true) {
            while(iterator.hasNext()) {
                ExcelExportEntity entity = (ExcelExportEntity) iterator.next();
                if (entity.isMergeVertical()) {
                    mergeMap.put(i, entity.getMergeRely());
                }

                if (entity.getList() != null) {
                    for(Iterator iterator1 = entity.getList().iterator(); iterator1.hasNext(); ++i) {
                        ExcelExportEntity inner = (ExcelExportEntity) iterator1.next();
                        if (inner.isMergeVertical()) {
                            mergeMap.put(i, inner.getMergeRely());
                        }
                    }
                } else {
                    ++i;
                }
            }

            return mergeMap;
        }
    }

    public CellStyle getStyles(boolean needOne, ExcelExportEntity entity) {
        return this.excelExportStyler.getStyles(needOne, entity);
    }

    public void mergeCells(Sheet sheet, List<ExcelExportEntity> excelParams, int titleHeight) {
        Map<Integer, int[]> mergeMap = this.getMergeDataMap(excelParams);
        PoiMergeCellUtil.mergeCells(sheet, mergeMap, titleHeight);
    }

    public void setCellWith(List<ExcelExportEntity> excelParams, Sheet sheet) {
        int index = 0;

        for (ExcelExportEntity excelParam : excelParams) {
            if (excelParam.getList() != null) {
                List<ExcelExportEntity> list = excelParam.getList();

                for (int j = 0; j < list.size(); ++j) {
                    sheet.setColumnWidth(index, (int) (256.0D * list.get(j).getWidth()));
                    ++index;
                }
            } else {
                sheet.setColumnWidth(index, (int) (256.0D * excelParam.getWidth()));
                ++index;
            }
        }

    }

    public void setCurrentIndex(int currentIndex) {
        this.currentIndex = currentIndex;
    }

    public void setExcelExportStyler(IExcelExportStyler excelExportStyler) {
        this.excelExportStyler = excelExportStyler;
    }

    public IExcelExportStyler getExcelExportStyler() {
        return this.excelExportStyler;
    }
}
